import asyncio
import json
from asyncio import Transport
from typing import List, Dict
import socketserver

from Lib import Language, Utils
from Lib.Message import MessageEvent, MessageSync
from Lib.Utils import Console
from Proto import Proto


#
# class Client:
#     def __init__(self, transport: WebSocketServerProtocol, detail=None):
#         if detail is None:
#             detail = {}
#         self.token = Utils.GeneratorToken()
#         self.transport: WebSocketServerProtocol = transport
#         self.peer = transport.peer
#         self.address = str(transport.peer)
#         self.detail = {**{}, **detail}
#
#     def Send(self, data, header=None):
#         data = MessageEvent(data, header)
#         Utils.Console(Language.CLIENT_SEND_MESSAGE.format(self.detail, data.ToJson()), color=35)
#         self.transport.sendMessage(data.ToBytes())
#
#     def ToJson(self):
#         return {
#                 "token"  : self.token,
#                 "peer"   : self.address.split(':')[1:],
#                 "address": self.address,
#                 "detail" : self.detail
#         }
#
#     def ToString(self):
#         return json.dumps(self.ToJson()).encode("utf-8")
#
#     def ToBytes(self):
#         return bytes(self.ToString(), encoding="utf-8")


class TcpProtocol(socketserver.BaseRequestHandler):
    def handle(self):
        try:
            while True:
                payload = self.request.recv(1024)
                if not payload:
                    Utils.Console(Language.CLIENT_RIVE_MESSAGE.format(payload))
                    message_event = json.loads(payload)
                    header = message_event['header']
                    data = message_event['data']
                    send_time = message_event['send_time']
                    proto = header['proto']
                    break
                # self.request.sendall(self.data.upper())
        except Exception as e:
            Utils.Console(Language.CLIENT_ABNORMAL_LEAVE_SERVER.format(self.client_address, e), "error", color=7)
        finally:
            self.request.close()

    def setup(self):
        Utils.Console(Language.CLIENT_ENTER_SERVER.format(self.client_address), "info", color=46)

    def finish(self):
        Utils.Console(Language.CLIENT_LEAVE_SERVER.format(self.client_address), "info", color=27)

    def onMessage(self, payload, isBinary):
        Utils.Console(Language.CLIENT_RIVE_MESSAGE.format(payload))
        message_event = json.loads(payload)
        header = message_event['header']
        data = message_event['data']
        send_time = message_event['send_time']
        proto = header['proto']
        if proto == Proto.USER_LOGIN.value:
            client = server.GetClientByUserId(data["user_id"])
            from autobahn.websocket.protocol import WebSocketProtocol
            if client:
                server.RemoveClientByClient(client)
                if client.transport.state == WebSocketProtocol.STATE_OPEN:
                    Utils.Console(Language.CLIENT_SAME_LOGIN.format(client.detail), "error")
                    client.Send({"msg": "检测到账号重复登录!"}, {"proto": Proto.USER_SAME_LOGIN.value})
            server.AddClient(self, data)
        elif proto == Proto.ROOM_CREATE.value:
            token = header['token']
            client = server.GetClientByToken(token)
            if client:
                # Utils.Console(Language.CLIENT_SAME_LOGIN.format(client.detail), "error")
                from Lib.RoomManager import room_manager
                from Lib.Room import Room
                room = Room(client, room_name=f"{client.detail['user_name']} 的房间")
                room_manager.AddRoom(room)
                client.Send({"room": room.ToJson()}, {"proto": Proto.ROOM_CREATE.value})


class UdpProtocol(asyncio.Protocol):
    def __init__(self):
        self.transport = None

    def connection_made(self, transport: Transport) -> None:
        self.transport = transport

    def datagram_received(self, data: bytes, addr, args=None) -> None:
        message = data.decode()
        print('Received %r from %s' % (message, addr))
        print('Send %r to %s' % (message, addr))
        self.transport.sendto(data, addr)


class Server:
    def __init__(self, config):
        self.loop = asyncio.get_event_loop()
        self.list_client: List[Client] = []
        self.host = config.get("base", "host")
        self.port = int(config.get("base", "tcp_port"))
        self.dict_room_server: Dict = {}
        self.list_sync: List[MessageSync] = []
        self.list_udp_port: List[int] = Utils.GeneratorIntList(int(config.get('base', 'udp_port_min')),
                                                               int(config.get('base', 'udp_port_max')))

    def GetOnlyUdpPort(self) -> [bool, int]:
        import random
        if len(self.list_udp_port) > 0:
            port = random.choice(self.list_udp_port)
            self.list_udp_port.remove(port)
            return port
        else:
            return False

    def CreateUdpServer(self, room_uuid, host, port):
        udp = self.loop.create_datagram_endpoint(UdpProtocol, (host, port))
        Console(f"正在启动 UDP 服务 :::===>>> {host} : {port}", "info", start="")
        Console(f"剩余可用 UDP 端口数 :: {len(self.list_udp_port)} 个", "info")
        self.dict_room_server[room_uuid] = udp
        self.loop.create_task(udp)
        return udp, (host, port)
    #
    # def AddClient(self, transport: WebSocketServerProtocol, detail=None):
    #     if detail is None:
    #         detail = {}
    #     client = Client(transport, detail)
    #     client.Send(client.ToJson(), {"proto": TypeProto.USER_LOGIN.value})
    #     self.list_client.append(client)
    #     self.SendMessageToClientList(self.list_client, {"user_list": self.GetAllListClient()},
    #                                  {"proto": TypeProto.USER_LIST.value})
    #
    # def GetClientByToken(self, token: str) -> [Client, None]:
    #     for client in self.list_client:
    #         if client.token == token:
    #             return client
    #     return None
    #
    # def GetClientByTransport(self, transport: Transport) -> [Client, None]:
    #     for client in self.list_client:
    #         if client.transport == transport:
    #             return client
    #     return None
    #
    # def GetClientByUserId(self, user_id: int):
    #     for client in self.list_client:
    #         if client.detail['user_id'] == user_id:
    #             return client
    #     return None
    #
    # def RemoveClientByToken(self, token: str):
    #     client = self.GetClientByToken(token)
    #     if client:
    #         client.transport.onClose(True, 0, "异常")
    #         self.list_client.remove(client)
    #         del client
    #
    # def RemoveClientByClient(self, client: Client):
    #     client.transport.onClose(True, 0, "异常")
    #     self.list_client.remove(client)
    #     del client
    #
    # def RemoveClientByTransport(self, transport: Transport):
    #     client = self.GetClientByTransport(transport)
    #     if client:
    #         client.transport.onClose(True, 0, "异常")
    #         self.list_client.remove(client)
    #         del client
    #
    # @staticmethod
    # def SendMessageToClientList(list_client: List[Client], data, header):
    #     [client.Send(data, header) for client in list_client]
    #
    # def GetAllListClient(self) -> List:
    #     return [client.ToJson() for client in self.list_client]
    #
    # def Start(self):
    #     Console(f"正在启动 TCP 服务 :::===>>> {self.host} : {self.port}", "info", start="")
    #     import uuid
    #     port = server.GetOnlyUdpPort()
    #     server.CreateUdpServer(uuid.uuid4().hex, "127.0.0.1", port)
    #     self.loop.create_task(self.tcp_server)
    #     self.loop.run_forever()


config = Utils.GetIniFile("./config.ini")
server = Server(config)
